package edu.uiuc.ncsa;

import edu.uiuc.ncsa.commons.NCSAKMLUtilityException;
import edu.uiuc.ncsa.commons.util.Util;
import edu.uiuc.ncsa.kmltordf.Converter;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.FileItemFactory;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.URL;
import java.net.URLConnection;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

/**
 * @author : Sachith Dhanushka (sachith.dhanushka@gmail.com)
 * @date : Jun 14, 2008 - 10:47:31 PM
 */
public class WebUtils {

    public static void download(String address, File localFile) {
        OutputStream out = null;
        URLConnection conn = null;
        InputStream in = null;
        try {
            URL url = new URL(address);
            out = new BufferedOutputStream(
                    new FileOutputStream(localFile));
            conn = url.openConnection();
            in = conn.getInputStream();
            byte[] buffer = new byte[1024];
            int numRead;
            long numWritten = 0;
            while ((numRead = in.read(buffer)) != -1) {
                out.write(buffer, 0, numRead);
                numWritten += numRead;
            }
            System.out.println(localFile + "\t" + numWritten);
        } catch (Exception exception) {
            exception.printStackTrace();
        } finally {
            try {
                if (in != null) {
                    in.close();
                }
                if (out != null) {
                    out.close();
                }
            } catch (IOException ioe) {
            }
        }
    }

    /**
     * @param zipFile
     * @return the converted kml file
     */
    public File processShapeZipFile(File zipFile) throws NCSAKMLUtilityException {
        // unzip the file
        String fileName = zipFile.getName();
        File targetLocation = new File(zipFile.getParentFile(), fileName.substring(0, fileName.lastIndexOf(".")));
        Util.unzip(zipFile, targetLocation);

        // find the shape file
        File rootFolder = targetLocation;


        File shapeFile = findShapeFile(rootFolder);
        if (shapeFile != null) {
            rootFolder = shapeFile.getParentFile();
        }

        // call shapefile to kml and get the kml file
        new ShapeFileConverter().convert(shapeFile, shapeFile.getParentFile());

        // return kml file
        return new File(shapeFile.getParentFile(), "output-2-2.kml");
    }

    private File findShapeFile(File rootFolder) {
        ShapeFileFilter shapeFileFilter = new ShapeFileFilter();
        File shapeFile = null;
        File[] files = rootFolder.listFiles(shapeFileFilter);
        if (files.length < 1) {
            // no shape files in this folder

            // search in each sub folder
            for (File file : rootFolder.listFiles()) {
                if (file.isDirectory()) {
                    shapeFile = findShapeFile(file);
                    if (shapeFile != null) {
                        return shapeFile;
                    }
                }
            }

            // we've got a shape file
        } else {
            return files[0];
        }
        return null;
    }

    public String getFileNameFromFileItem(FileItem item) {
        String name = item.getName();
        int i = name.lastIndexOf("\\");
        if (i == -1) {
            i = name.lastIndexOf("/");
        }

        name = i > -1 ? name.substring(i + 1) : name;

        return name;
    }

    public static void process(HttpServletRequest request) {
        // Create a factory for disk-based file items
        FileItemFactory factory = new DiskFileItemFactory();

        // Create a new file upload handler
        ServletFileUpload upload = new ServletFileUpload(factory);

        File tempFolder = new File("temp");

        try {
            List<FileItem> items = upload.parseRequest(request);
            File kmlFile = null;
            String source = "";
            String fileURL = "";
            for (FileItem item : items) {
                if (!item.isFormField()) {
                    File shapeFile = new File(tempFolder, "rdf2kmlTemp.rdf");
                    item.write(shapeFile);
                    kmlFile = new WebUtils().processShapeZipFile(shapeFile);
                } else {
                    String name = item.getFieldName();
                    String value = item.getString();
                    if ("source".equals(name)) {
                        source = value;
                    } else if (name.equals("url")) {
                        fileURL = value;
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
        }

    }

    public static File convertKMLToRDF(File inputKmlFile, String parentURL) throws NCSAKMLUtilityException {

        try {

            File temp = new File("temp.rdf");

            Converter kmlToRdfConverter = new Converter();
            kmlToRdfConverter.convert(new FileInputStream(inputKmlFile), new FileOutputStream(temp), parentURL);


            return temp;
        } catch (FileNotFoundException e) {
            throw new NCSAKMLUtilityException(e);
        }
    }

//    public static File convertKMLToRDF(File inputKmlFile) throws NCSAKMLUtilityException {
//
//        try {
//
//            String xslFileLocation = "../webapps/ncsakml/kml2rdf.xsl";
//
//            StreamSource source = new StreamSource(inputKmlFile);
//            StreamSource stylesource = new StreamSource(xslFileLocation);
//
//            TransformerFactory factory = TransformerFactory.newInstance();
//            Transformer transformer = factory.newTransformer(stylesource);
//
//            File temp = new File("temp.rdf");
//
//            StreamResult result = new StreamResult(temp);
//            transformer.transform(source, result);
//            return temp;
//        } catch (TransformerException e) {
//            throw new NCSAKMLUtilityException(e);
//        }
//    }


    public static GetFileResult getSourceFile(HttpServletRequest request, String tempFolderLoc) throws Exception {
        File inputKmlFile = null;

        // Create a factory for disk-based file items
        FileItemFactory factory = new DiskFileItemFactory();

        // Create a new file upload handler
        ServletFileUpload upload = new ServletFileUpload(factory);

        File tempFolder = new File(tempFolderLoc);
        if (!tempFolder.isDirectory()) {
            tempFolder.mkdir();
        }

        List<FileItem> items = upload.parseRequest(request);
        String source = "";
        String fileURL = "";
        for (FileItem item : items) {
            if (!item.isFormField()) {
                inputKmlFile = new File(tempFolder, "myTemp.xml");
                item.write(inputKmlFile);
            } else {
                String name = item.getFieldName();
                String value = item.getString();
                if ("source".equals(name)) {
                    source = value;
                } else if (name.equals("url")) {
                    fileURL = value;
                }
            }
        }

        if (source.equals("url")) {
            // get the file
            int lastSlashIndex = fileURL.lastIndexOf('/');
            if (lastSlashIndex >= 0 &&
                    lastSlashIndex < fileURL.length() - 1) {
                inputKmlFile = new File(tempFolderLoc, fileURL.substring(lastSlashIndex + 1));
                WebUtils.download(fileURL, inputKmlFile);
            }
        }

        return new GetFileResult(inputKmlFile, fileURL);
    }

    public static File convertRDFToKML(File inputRDFFile) throws NCSAKMLUtilityException {
        try {

            File temp = new File("temp.kml");

            edu.uiuc.ncsa.rdftokml.Converter rdfToKMLConverter = new edu.uiuc.ncsa.rdftokml.Converter();
            rdfToKMLConverter.convert(new FileInputStream(inputRDFFile), new FileOutputStream(temp));


            return temp;
        } catch (FileNotFoundException e) {
            throw new NCSAKMLUtilityException(e);
        }
    }

    public static File convertGTToKML(File geoTiffFile, String tempFolder, String geoTIFFScriptPath, String fwToolsHome) throws NCSAKMLUtilityException {
        Process p = null;

        File fout = new File(tempFolder, "GeoTIFF");
        if (!fout.isDirectory()) {
            fout.mkdir();
        }

        String cmd = "sh " + geoTIFFScriptPath + " " + geoTiffFile + " " + fout.getAbsolutePath() + " " + fwToolsHome;

        try {
            System.out.println("Executing gdal with command >>" + cmd + "<<");
            p = Runtime.getRuntime().exec(cmd);

            p.waitFor();  //need to wait so can read file

            if (!fout.exists()) {
                throw new IOException("Transformed file not created: " + fout.getAbsolutePath());
            }
            System.out.println("Transformed file created: " + fout.getAbsolutePath());

        } catch (InterruptedException e) {
            throw new NCSAKMLUtilityException(e);
        } catch (IOException e) {
            throw new NCSAKMLUtilityException(e);
        }

        File resultZipFile = new File(fout, "geotiff2kml.zip");
        // zip the folder
        //zipFolder(fout.getAbsolutePath(), resultZipFile.getAbsolutePath());

        return resultZipFile;
    }

    public static void convertShapeFile(HttpServletRequest request, HttpServletResponse response, String output, String tempFolderLoc) {
        File shapeFile;

//        if ("upload".equals(source) ) {

        // Create a factory for disk-based file items
        FileItemFactory factory = new DiskFileItemFactory();

        // Create a new file upload handler
        ServletFileUpload upload = new ServletFileUpload(factory);

        File tempFolder = new File(tempFolderLoc);
        if (!tempFolder.isDirectory()) {
            tempFolder.mkdir();
        }

        // Parse the request
        try {
            List<FileItem> items = upload.parseRequest(request);
            File kmlFile = null;
            String source = "";
            String fileURL = "";
            for (FileItem item : items) {
                if (!item.isFormField() && !("datafile".equals(item.getFieldName()) && item.getSize() == 0)) {
                    shapeFile = new File(tempFolder, "shapefile.zip");
                    item.write(shapeFile);
                    kmlFile = new WebUtils().processShapeZipFile(shapeFile);
                    shapeFile.delete();
                } else {
                    String name = item.getFieldName();
                    String value = item.getString();
                    if ("source".equals(name)) {
                        source = value;
                    } else if (name.equals("url")) {
                        fileURL = value;
                    } else if (name.equals("output")) {
                        output = value;
                    }
                }
            }

            if (source.equals("url")) {
                // get the file
                int lastSlashIndex = fileURL.lastIndexOf('/');
                if (lastSlashIndex >= 0 &&
                        lastSlashIndex < fileURL.length() - 1) {
                    File downloadedZipFile = new File(tempFolderLoc, fileURL.substring(lastSlashIndex + 1));
                    WebUtils.download(fileURL, downloadedZipFile);

                    System.out.println("Downloaded the file ...");
                    System.out.println("Processing file ...");
                    kmlFile = new WebUtils().processShapeZipFile(downloadedZipFile);
                    System.out.println("File Processed ...");
                }
            }
// else {
//                System.err.println("Could not figure out local file name for " +
//                        fileURL);
//            }
            // send the kml file
            if (!"gearth".equals(output)) {
                ResponseHandler.sendTextFileToBrowser(kmlFile, response);
            } else {
                ResponseHandler.showInGEarth(kmlFile, request, response);
            }
        } catch (Exception
                e) {

            ResponseHandler.sendError(response, "Error in processing the request. " + e.getMessage());
        }
    }


    class ShapeFileFilter implements FilenameFilter {

        public boolean accept(File file, String s) {
            return s.endsWith(".shp");
        }
    }


    /**
     * Zip the srcFolder into the destFileZipFile. All the folder subtree of the src folder is added to the destZipFile
     * archive.
     * <p/>
     * TODO handle the usecase of srcFolder being en file.
     *
     * @param srcFolder   String, the path of the srcFolder
     * @param destZipFile String, the path of the destination zipFile. This file will be created or erased.
     */
    public static void zipFolder(String srcFolder, String destZipFile) {
        ZipOutputStream zip = null;
        FileOutputStream fileWriter = null;
        try {
            fileWriter = new FileOutputStream(destZipFile);
            zip = new ZipOutputStream(fileWriter);
        }
        catch (Exception ex) {
            ex.printStackTrace();
            System.exit(0);
        }

        addFolderToZip("", srcFolder, zip);
        try {
            zip.flush();
            zip.close();
        }
        catch (Exception ex) {
            ex.printStackTrace();
        }
    }

    /**
     * Write the content of srcFile in a new ZipEntry, named path+srcFile, of the zip stream. The result
     * is that the srcFile will be in the path folder in the generated archive.
     *
     * @param path    String, the relatif path with the root archive.
     * @param srcFile String, the absolute path of the file to add
     * @param zip     ZipOutputStram, the stream to use to write the given file.
     */
    static private void addToZip(String path, String srcFile, ZipOutputStream zip) {

        File folder = new File(srcFile);
        if (folder.isDirectory()) {
            addFolderToZip(path, srcFile, zip);
        } else {
// Transfer bytes from in to out
            byte[] buf = new byte[1024];
            int len;
            try {
                FileInputStream in = new FileInputStream(srcFile);
                zip.putNextEntry(new ZipEntry(path + "/" + folder.getName()));
                while ((len = in.read(buf)) > 0) {
                    zip.write(buf, 0, len);
                }
            }
            catch (Exception ex) {
                ex.printStackTrace();
            }
        }
    }

    /**
     * add the srcFolder to the zip stream.
     *
     * @param path      String, the relatif path with the root archive.
     * @param srcFolder String, the absolute path of the file to add
     * @param zip       ZipOutputStram, the stream to use to write the given file.
     */
    static private void addFolderToZip(String path, String srcFolder, ZipOutputStream zip) {
        File folder = new File(srcFolder);
        String fileListe[] = folder.list();
        try {
            int i = 0;
            while (true) {
                addToZip(path + "/" + folder.getName(), srcFolder + "/" + fileListe[i], zip);
                i++;
            }
        }
        catch (Exception ex) {
        }
    }


    public static void main(String[] args) {
        try {
            File kmlFile = new WebUtils().processShapeZipFile(new File("/tmp/saltstrm.zip"));

            System.out.println("kmlFile.getAbsolutePath() = " + kmlFile.getAbsolutePath());

        } catch (Exception e) {
            e.printStackTrace();
        }

    }
}
